package dh.abs.info;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import dh.abs.Parameters;
import dh.abs.util.SuspendableCollection;

/**
 * @author denialhumanity
 *
 */
public class InformationDiffusionParameters implements Parameters {
	/** number of agents */
	private Integer numAgents;
	
	/** number of information */
	private Integer numInformation;
	
	/** Step number to stop simulation */
	private Integer maxStep;
	
	/** current step number */
	private Integer currentStep;
	
	/** rate array of number of agents */
	private List<Double> categorization;
	
	/** capacity array of agents */
	private List<Integer> informationCapacity;
	
	/** whether this simulation resumes from files or not */
	private Boolean isResume;
	
	/** whether this simulation logs the state of each agent */
	private Boolean isAgentLogging;
	
	/** whether this simulation logs the state of environment */
	private Boolean isEnvLogging;
	
	/** interval of logging */
	private Integer loggingInterval;
	
	/** prefix of log stored path */
	private String logPrefix;
	
	/** whether this simulation suspends when stopped */
	private Boolean isSuspend;
	
	/** path of directory that stores files required for suspend*/
	private String suspendDir;
	
	/** path of directory that stores file required for resume */
	private String resumeDir;
	
	/** path of file that stores final result of this simulation */
	private String resultFile;
	

	/**
	 * Default constructor.
	 * This constructor initializes all the parameters by default values.
	 */
	public InformationDiffusionParameters() {
		numAgents = 100;
		numInformation = 100;
		maxStep = 500000;
		currentStep = 0;
		Double[] tmpCateg = {0.02, 0.18, 0.8};
		categorization = new ArrayList<Double>();
		int[] tmpCapac = {100,10,1};
		informationCapacity = new ArrayList<Integer>();
		for(int i = 0; i < tmpCateg.length; i++) {
			categorization.add(tmpCateg[i]);
			informationCapacity.add(tmpCapac[i]);
		}
		isResume = false;
		loggingInterval = 1;
		isEnvLogging = true;
		isAgentLogging = true;
		logPrefix = ".";
		isSuspend = true;
		suspendDir = "suspend_data";
	}
	
	/**
	 * This constructor load parameters from Properties instance.
	 * @param prop
	 * @throws IllegalParameterException
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public InformationDiffusionParameters(Properties prop) throws IllegalParameterException {
		Field[] fields = InformationDiffusionParameters.class.getDeclaredFields();
		
		for(Field f: fields) {
			try {
				if(f.getType().equals(List.class)) {
					String[] strs = prop.getProperty(f.getName()).split(",");
					List tmpList = new ArrayList();
					Type t = ((ParameterizedType)(f.getGenericType())).getActualTypeArguments()[0];
					Class c = Class.forName(t.toString().split(" ")[1]);
					for(String str: strs) {
						tmpList.add(c.getConstructor(String.class).newInstance(str));
					}
					f.set(this, tmpList);
				} else {
					f.set(this, f.getType().getConstructor(String.class).newInstance(prop.getProperty(f.getName())));
				}
			} catch (IllegalArgumentException e) {
				throw new IllegalParameterException("Illegal Parameters for " + f.getName(), e);
			} catch (IllegalAccessException e) {
				throw new IllegalParameterException("Illegal Parameters for " + f.getName(), e);
			} catch (InstantiationException e) {
				throw new IllegalParameterException("Illegal Parameters for " + f.getName(), e);
			} catch (InvocationTargetException e) {
				throw new IllegalParameterException("Illegal Parameters for " + f.getName(), e);
			} catch (NoSuchMethodException e) {
				throw new IllegalParameterException("Illegal Parameters for " + f.getName(), e);
			} catch (SecurityException e) {
				throw new IllegalParameterException("Illegal Parameters for " + f.getName(), e);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * @param filename
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws IOException
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void store2xml(String filename) throws IllegalArgumentException, IllegalAccessException, IOException {
		Properties prop = new Properties();
		Field[] fields = InformationDiffusionParameters.class.getDeclaredFields();
		
		for(Field f: fields) {
			if(f.getType().equals(List.class)) {
				prop.setProperty(f.getName(), (new SuspendableCollection((ArrayList)f.get(this))).toString());
			} else {
				prop.setProperty(f.getName(), String.valueOf(f.get(this)));
			}
		}
		
		File file = new File(filename);
		FileOutputStream fos = new FileOutputStream(file);
		
		prop.storeToXML(fos, "default parameters", "UTF8");
		
		fos.close();
	}
	
	public static void main(String[] args) {
		InformationDiffusionParameters param = new InformationDiffusionParameters();
		String str = "default_param.xml";
		if(args.length == 1) {
			str = args[0];
		}
		try {
			param.store2xml(str);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @return the numAgents
	 */
	public Integer getNumAgents() {
		return numAgents;
	}

	/**
	 * @param numAgents the numAgents to set
	 */
	public void setNumAgents(Integer numAgents) {
		this.numAgents = numAgents;
	}

	/**
	 * @return the numInformation
	 */
	public Integer getNumInformation() {
		return numInformation;
	}

	/**
	 * @param numInformation the numInformation to set
	 */
	public void setNumInformation(Integer numInformation) {
		this.numInformation = numInformation;
	}

	/**
	 * @return the maxStep
	 */
	public Integer getMaxStep() {
		return maxStep;
	}

	/**
	 * @param maxStep the maxStep to set
	 */
	public void setMaxStep(Integer maxStep) {
		this.maxStep = maxStep;
	}

	/**
	 * @return the currentStep
	 */
	public Integer getCurrentStep() {
		return currentStep;
	}

	/**
	 * @param currentStep the currentStep to set
	 */
	public void setCurrentStep(Integer currentStep) {
		this.currentStep = currentStep;
	}

	/**
	 * @return the categorization
	 */
	public List<Double> getCategorization() {
		return categorization;
	}

	/**
	 * @param categorization the categorization to set
	 */
	public void setCategorization(List<Double> categorization) {
		this.categorization = categorization;
	}

	/**
	 * @return the informationCapacity
	 */
	public List<Integer> getInformationCapacity() {
		return informationCapacity;
	}

	/**
	 * @param informationCapacity the informationCapacity to set
	 */
	public void setInformationCapacity(List<Integer> informationCapacity) {
		this.informationCapacity = informationCapacity;
	}

	/**
	 * @return the isAgentSpecified
	 */
	public Boolean isResume() {
		return isResume;
	}

	/**
	 * @param isAgentSpecified the isAgentSpecified to set
	 */
	public void setResume(Boolean isResume) {
		this.isResume = isResume;
	}

	/**
	 * @return the isAgentLogging
	 */
	public Boolean isAgentLogging() {
		return isAgentLogging;
	}

	/**
	 * @param isAgentLogging the isAgentLogging to set
	 */
	public void setAgentLogging(Boolean isAgentLogging) {
		this.isAgentLogging = isAgentLogging;
	}

	/**
	 * @return the isEnvLogging
	 */
	public Boolean isEnvLogging() {
		return isEnvLogging;
	}

	/**
	 * @param isEnvLogging the isEnvLogging to set
	 */
	public void setEnvLogging(Boolean isEnvLogging) {
		this.isEnvLogging = isEnvLogging;
	}

	/**
	 * @return the loggingInterval
	 */
	public Integer getLoggingInterval() {
		return loggingInterval;
	}

	/**
	 * @param loggingInterval the loggingInterval to set
	 */
	public void setLoggingInterval(Integer loggingInterval) {
		this.loggingInterval = loggingInterval;
	}

	/**
	 * @return the logPrefix
	 */
	public String getLogPrefix() {
		return logPrefix;
	}

	/**
	 * @param logPrefix the logPrefix to set
	 */
	public void setLogPrefix(String logPrefix) {
		this.logPrefix = logPrefix;
	}

	/**
	 * @return the isSuspend
	 */
	public Boolean isSuspend() {
		return isSuspend;
	}

	/**
	 * @param isSuspend the isSuspend to set
	 */
	public void setSuspend(Boolean isSuspend) {
		this.isSuspend = isSuspend;
	}

	/**
	 * @return the suspendDir
	 */
	public String getSuspendDir() {
		return suspendDir;
	}

	/**
	 * @param suspendDir the suspendDir to set
	 */
	public void setSuspendDir(String suspendDir) {
		this.suspendDir = suspendDir;
	}

	/**
	 * @return the resumeDir
	 */
	public String getResumeDir() {
		return resumeDir;
	}

	/**
	 * @param resumeDir the resumeDir to set
	 */
	public void setResumeDir(String resumeDir) {
		this.resumeDir = resumeDir;
	}

	/**
	 * @return the resultFile
	 */
	public String getResultFile() {
		return resultFile;
	}

	/**
	 * @param resultFile the resultFile to set
	 */
	public void setResultFile(String resultFile) {
		this.resultFile = resultFile;
	}
}
